/*

Design Pattern Automation Toolkit.
Application to create applications with emphasis on Design patterns.
And support for round trip engineering.
Copyright (C) 2004 Vineeth Neelakant. nvineeth@gmail.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Printing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using DesignPatternDefinitions;
using BusinessLayer;
using InfoClasses;
using System.Xml;
using dpatoolkit.UILayer;

namespace dpatoolkit.UILayer
{

	/// <summary>
	/// Manages project related activities like adding new componenets, closing,
	/// updating the tree view etc.
	/// </summary>
	/// <remarks>
	/// 	created by - vineeth n
	/// 	created on - 11/30/2004 1:07:47 PM
	/// </remarks>
	
	public class ProjectUIManager  : IUIProjectUnit
	{
		
		IDesignPatternSubject subject;
		TreeNode projNode;
		ContextMenu rootMenu,compMenu;
		string filePath=String.Empty;
		public event ProjectClosing ProjectClosing;

		public ProjectUIManager()
		{
			this.CreateTreeNodeMenus();
			this.UpdateUI();
		}
		
		#region Helper Methods
		private void CreateProjectTreeNode(string projName)
		{
			
			this.projNode = new TreeNode(projName,0,0);
			DPAppData.MDIManager.ProjectTreeView.Nodes.Add(this.projNode);
			this.projNode.Tag = this;
		}

        private void CreateComponentTreeNode(string compName)
		{
			
			TreeNode compNode = new TreeNode(compName,1,1);
            compNode.Tag = this;
			this.projNode.Nodes.Add(compNode);
			DPAppData.MDIManager.ProjectTreeView.SelectedNode=compNode;
		}

		private void CreateTreeNodeMenus()
		{
			rootMenu = new ContextMenu();
			MenuItem newCompMenu = new MenuItem("Add New Component (Class Diagram)"),
				editProjCompMenu = new MenuItem("Edit Project Details"),
				saveProjCompMenu = new MenuItem("Save Project"),
				genSnapShotCompMenu = new MenuItem("Generate snapshot"),
                codeGenMenu = new MenuItem("Generate Code"),
				closeMenuItem = new MenuItem("Close Project");
			
			newCompMenu.Click+=new EventHandler(this.NewComponentMenuHandler);
			editProjCompMenu.Click+=new EventHandler(this.EditProjectMenuHandler);
			saveProjCompMenu.Click+=new EventHandler(this.SaveProjectMenuHandler);
			genSnapShotCompMenu.Click+=new EventHandler(this.GenerateSnapShotMenuHandler);
            codeGenMenu.Click += new EventHandler(codeGenMenu_Click);
			closeMenuItem.Click+=new EventHandler(this.CloseProjectMenuHandler);
			
			rootMenu.MenuItems.Add(newCompMenu);
			rootMenu.MenuItems.Add(editProjCompMenu);
			rootMenu.MenuItems.Add(saveProjCompMenu);
			rootMenu.MenuItems.Add(genSnapShotCompMenu);
            rootMenu.MenuItems.Add(codeGenMenu);
			rootMenu.MenuItems.Add(closeMenuItem);
			
			compMenu = new ContextMenu();
			MenuItem removeCompMenuItem = new MenuItem("RemoveComponent"),
				editCompMenuItem = new MenuItem("Edit Component");
			
			editCompMenuItem.Click+=new EventHandler(this.EditComponentMenuHandler);
			removeCompMenuItem.Click+=new EventHandler(this.RemoveComponentMenuHandler);
			
			compMenu.MenuItems.Add(editCompMenuItem);
			compMenu.MenuItems.Add(removeCompMenuItem);
			
		}

        void codeGenMenu_Click(object sender, EventArgs e)
        {
            try
            {
                CodeGenPluginForm form = new CodeGenPluginForm();
                form.ShowDialog();
                if (form.DialogResult == DialogResult.Cancel)
                    return;
                IDPACodeGenerator codegen = form.CodeGenerator;
                if (codegen != null)
                {
                    codegen.GenerateCode(this.subject.GetProject());
                    DPAppData.MDIManager.ShowStatusMessage("Code Generation Plugin invocation complete.", 5000);
                }
            }
            catch (Exception exp)
            {
                ErrorMessage.Show("Code Generator Exception:" + exp.Message);
            }
        }
		#endregion
		
		
		#region IUIProjectUnit Members

        public bool New(ProjectComponentType compType)
		{
			AddProjectForm apf = new AddProjectForm("NewProject",null,null,null);
			apf.ShowDialog();
			apf.Text="Enter Project Details";
			if( apf.DialogResult == DialogResult.Cancel || apf.DPName==string.Empty)
				return false;
			
			this.subject= new ApplicationDataSet();
			
			IProject proj = new InfoClasses.ProjectInfo();
			proj.ProjectName=apf.DPName;
			proj.ProjectAuthor=apf.Author;
			proj.ProjectSummary= apf.Summary;
			
			if( this.subject.CreateNewProject(proj) )
			{
				this.CreateProjectTreeNode(proj.ProjectName);
				
				//if ( MessageBox.Show(null,"New Project with name "+ proj.ProjectName+" has been created, would you like to add a component to this project?","Add new component?",MessageBoxButtons.OKCancel,MessageBoxIcon.Question)
				//	== DialogResult.OK)
				
                this.AddNewComponent();
				return true;
			}
			ErrorMessage.Show("Couldnot create the new project","Error");
			return false;
		}

		public bool Open(string filename)
		{
			if(this.subject!=null)
				this.subject.Dispose();

			this.subject = new ApplicationDataSet();
            if (!this.subject.ReadProject(filename))
				return false;
            this.filePath = filename;
			IProject proj = this.subject.GetProject();				
			return this.Open(proj);
				
		}
		public bool Open( IDesignPatternSubject newSub)
		{
			if(this.subject!=null)
				this.subject.Dispose();
			this.subject = newSub;
			IProject proj = this.subject.GetProject();				
			return this.Open(proj);
			
		}
		private bool Open(IProject proj)
		{
			if( this.projNode==null)
				this.CreateProjectTreeNode(proj.ProjectName);
			
			foreach( IProjectComponent comp in proj.Components)
				this.CreateNewComponent(comp.ComponentName,true);
			this.subject.UpdateComponents();
			return true;
		}
        public bool Save(bool confirm)
		{
            if (confirm &&
                MessageBox.Show(
                "Save Changes to Project " + this.subject.GetProject().ProjectName + " before closing?",
                "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No )
                return false;

            if (this.filePath == string.Empty)
            {
                SaveFileDialog saveDialog = new SaveFileDialog();
                saveDialog.Title = "Save Project " + this.subject.GetProject().ProjectName;
                saveDialog.Filter = "xml files (*.xml)|*.xml";
                saveDialog.FilterIndex = 1; // select xml
                saveDialog.OverwritePrompt = true;
                saveDialog.AddExtension = true;
                saveDialog.DefaultExt = "*.xml";

                if (saveDialog.ShowDialog() == DialogResult.Cancel)
                    return false;

                this.filePath = saveDialog.FileName;
            }
                    
			if( this.subject.WriteProject(this.filePath)) 
				DPAppData.MDIManager.ShowStatusMessage(this.projNode.Text+" saved!",4000);
            else
                DPAppData.MDIManager.ShowStatusMessage(this.projNode.Text + " could not be saved!" );

			return true;
		}
		public void Close()
		{

            this.Save(/* ask confirmation */ true);
			// close the components
			foreach (IMdiChild component in DPAppData.MDIManager.Documents)
			{
				if( component.Project == this )
					component.Close();
			}
			try
			{
				this.projNode.Nodes.Clear();
				if( !DPAppData.MDIManager.ProjectTreeView.IsDisposed)
					DPAppData.MDIManager.ProjectTreeView.Nodes.Remove(this.projNode);
				this.subject.Dispose();
			}
			catch
			{
			}
		}

		public bool SaveAs()
		{
            SaveFileDialog saveAsFileDialog = new SaveFileDialog();
            saveAsFileDialog.Title = "Save As...";
            saveAsFileDialog.OverwritePrompt = true;
            saveAsFileDialog.SupportMultiDottedExtensions = true;
            saveAsFileDialog.ValidateNames = true;
            saveAsFileDialog.DereferenceLinks = true;
            
            if(saveAsFileDialog.ShowDialog() == DialogResult.OK)
            {
                if (this.subject.WriteProject(saveAsFileDialog.FileName))
                    DPAppData.MDIManager.ShowStatusMessage(this.projNode.Text + " saved!", 4000);
                else
                    DPAppData.MDIManager.ShowStatusMessage(this.projNode.Text + " could not be saved!");

            }
            return true;
		}

		public string File()
		{
            return this.filePath;
		}

		public string ProjectName()
		{
			return this.subject.GetProject().ProjectName;
		}
        
        ArrayList docsToPrint = new ArrayList();

		public bool Command(MdiCommand command)
		{
			if (command == MdiCommand.AddNewComponent)
			{
				this.AddNewComponent();
			}
			else if(command == MdiCommand.GenerateSnapshot)
			{
				this.GenerateImage();
			}
			else if(command == MdiCommand.PrintAllComponents)
			{
                this.docsToPrint.Clear();
				foreach(IMdiChild child in DPAppData.MDIManager.Documents)
				{
                    if (child.Project == this)
                        this.docsToPrint.Add(child);
				}
                if (this.docsToPrint.Count == 0)
                {
                    MessageBox.Show("No Components found in " + this.ProjectName() + " to print.",
                        "Print",MessageBoxButtons.OK,
                        MessageBoxIcon.Exclamation);
                    return false;
                }
                else
                {
                    PrintDialog printDialog = new PrintDialog();
                    PrintDocument printDoc = new PrintDocument();
                    printDoc.PrintPage += new PrintPageEventHandler(printDoc_PrintPage);
                    // - print dialog.
                    //printDialog.AllowCurrentPage = true;
                    printDialog.AllowPrintToFile = true;
                    printDialog.AllowSelection = false;
                    printDialog.AllowSomePages = false;
                    printDialog.Document = printDoc;

                    // If the result is OK then print the document.
                    if (printDialog.ShowDialog() == DialogResult.OK)
                    {
                        printDoc.Print();
                    }
                }
			}
			return true;
		}

        void printDoc_PrintPage(object sender, PrintPageEventArgs e)
        {
            try
            {
                Graphics g = e.Graphics;
                IMdiChild child = this.docsToPrint[0] as IMdiChild;
                child.Print(g);
                this.docsToPrint.RemoveAt(0);
                if (0 == this.docsToPrint.Count)
                    e.HasMorePages = false;
                else
                    e.HasMorePages = true;
            }
            catch (Exception exp)
            {
                MessageBox.Show("Exception caught during printing:\n" + exp.ToString());
            }
        }

		private void AddNewComponent()
		{

			AddProjectForm apf = new AddProjectForm("NewComponent",null,null,new string[]{"ComponentName","ComponentAuthor","Summary"});
			apf.Text = "Enter Component Details";
			apf.ShowDialog();
			if( apf.DialogResult == DialogResult.Cancel || apf.DPName=="")
				return ;
				
			IProjectComponent comp = new InfoClasses.ProjectComponentInfo();
			comp.ComponentName = apf.DPName;
			comp.ComponentAuthor = apf.Author;
			comp.ComponentSummary = apf.Summary;
			if( this.subject.CreateNewComponent(comp) )
			{
				this.CreateNewComponent(comp.ComponentName,true);
			}
		}

		private void CreateNewComponent(string compName,bool createTreeNode)
		{
			IMdiChild newComponent = new ComponentForm(compName,subject);
			//set the tag to the proj manager..forIdentification.
			newComponent.Project=this;
			newComponent.Title = compName;
			DPAppData.MDIManager.AddDocument(newComponent);
			if(createTreeNode == true)
                this.CreateComponentTreeNode(compName);
			
		}

		public void UpdateUI()
		{
			/* Update the tree View */
			if(this.subject!=null)
			{
				this.CreateProjectTreeNode(this.subject.GetProject().ProjectName);
				foreach (IProjectComponent projComp in this.subject.GetProject().Components)
					this.CreateComponentTreeNode(projComp.ComponentName);
			}
			DPAppData.MDIManager.ProjectTreeView.MouseDown+=new MouseEventHandler(treeViewMouseDown);
			DPAppData.MDIManager.ProjectTreeView.DoubleClick+=new EventHandler(treeViewDoubleClick);
			
		}

		public void Recover()
		{
			// TODO:  Add ProjectUIManager.Recover implementation
		}
		

		#endregion
	
		
		public void GenerateImage()
		{
			FolderBrowserDialog dialog = new FolderBrowserDialog();
			dialog.Description="The snapshot of each component in the project "+ this.subject.GetProject().ProjectName +" will be saved here";
			if( dialog.ShowDialog() == DialogResult.Cancel)
				return;
            foreach (IMdiChild child in DPAppData.MDIManager.Documents)
				if (child.Project==this)
					child.SaveAsImage(dialog.SelectedPath+"/"+child.Title+".jpg");
		}
		
		public ProjectType ProjectType
		{
			get
			{
				return ProjectType.Project;
			}
		}
		public IDesignPatternSubject Subject
		{
			get
			{
				return this.subject;
			}
			set
			{
				this.subject=value as IDesignPatternSubject;
			}
		}
		
		public ContextMenu GetContextMenu(TreeNode node)
		{
			
			if(this.projNode==node)
				return rootMenu;
			else if(this.projNode.Nodes.Contains(node))
				return this.compMenu;
			return null;
		}
		
		#region tree view event handlers
		
		private void treeViewMouseDown(object sender, MouseEventArgs e)
		{
			TreeView tv = sender as TreeView;
			TreeNode clickedNode = tv.GetNodeAt(e.X,e.Y);

			tv.SelectedNode = clickedNode;

			if(e.Button==MouseButtons.Right)
			{
				
				if(this.projNode==tv.SelectedNode)
					this.rootMenu.Show(tv,new Point(e.X,e.Y));
				
				if ( this.projNode.Nodes.Contains(tv.SelectedNode) )
				{
					this.compMenu.Show(tv,new Point(e.X,e.Y));
				}
			}
		}

		private void treeViewDoubleClick(object sender, EventArgs e)
		{
			TreeView tv = sender as TreeView;
			TreeNode node = tv.SelectedNode;
			if(node == null || node.Tag != this || node.Parent == null)
				return; //nothing needs to be done.

            foreach (IMdiChild child in DPAppData.MDIManager.Documents)
			{
				if(child.Project == this)
				{
					if( child.Title == node.Text )
					{
                        DPAppData.MDIManager.CurrentDocument = child;
					}
				}
			}
            //if ((belongsToMe && !docOpen) || DPAppData.MDIManager.Documents.Count == 0) 
            //{
            //    //then create a new component, because it has been closed by the user.
            //    this.CreateNewComponent(node.Text,false);
            //    this.subject.UpdateComponent(node.Text);
            //}
		}
		
		#endregion

		#region Menu Handlers
		private void NewComponentMenuHandler( object sender, EventArgs args)
		{
			this.AddNewComponent();
		}
		private void EditProjectMenuHandler( object sender, EventArgs args)
		{
			IProject _proj = this.subject.ProjectDetails;
			AddProjectForm apf = new AddProjectForm(_proj.ProjectName,_proj.ProjectAuthor,_proj.ProjectSummary,null);
			apf.ShowDialog();
			apf.Text="Edit Project Details";
			if( apf.DialogResult == DialogResult.Cancel || apf.DPName==string.Empty)
				return ;
			IProject proj = new InfoClasses.ProjectInfo();
			proj.ProjectName=apf.DPName;
			proj.ProjectAuthor=apf.Author;
			proj.ProjectSummary= apf.Summary;
			this.subject.ProjectDetails=proj;
			this.projNode.Text=proj.ProjectName;
		}
		private void SaveProjectMenuHandler(object sender, EventArgs args)
		{
			this.Save(false);
		}
		private void GenerateSnapShotMenuHandler(object sender, EventArgs args)
		{
			this.GenerateImage();
		}
		private void CloseProjectMenuHandler(object sender, EventArgs args)
		{
			ProjectClosing(this);
			this.Close();
		}
		private void RemoveComponentMenuHandler(object sender, EventArgs args)
		{
			string delCompName = DPAppData.MDIManager.ProjectTreeView.SelectedNode.Text;
			if( MessageBox.Show("Are you sure you want to delete the component "+ delCompName +" ? \n All the classes in this component, the classes imported from this component by other components will also be removed...",
				"Confirmation",
				MessageBoxButtons.OKCancel,
				MessageBoxIcon.Question
				) == DialogResult.OK
				)
			{
				if ( this.subject.DeleteComponent( delCompName ) )
				{
					
					//find the child and close it.
					foreach (IMdiChild component in DPAppData.MDIManager.Documents)
					{
                        if ((component.Project == this) && (component.Title == delCompName))
                        {
                            DPAppData.MDIManager.RemoveDocument(component);
                        }
					}
					this.subject.UpdateComponents();
					this.projNode.Nodes.Remove(DPAppData.MDIManager.ProjectTreeView.SelectedNode);
				}
				else
				{
					ErrorMessage.Show("Internal Error: Couldnot delete the component " + delCompName,"Please report this error");
				}
			}

		}
		private void EditComponentMenuHandler(object sender, EventArgs args)
		{
            string selectedComponent = DPAppData.MDIManager.ProjectTreeView.SelectedNode.Text;
            IProjectComponent pc = this.subject.ProjectDetails.GetComponent(selectedComponent);
			AddProjectForm apf = new AddProjectForm(pc.ComponentName,pc.ComponentAuthor,pc.ComponentSummary,new string[]{"ComponentName","ComponentAuthor","Summary"});
            apf.Text = "Edit Component Details";
            apf.ShowDialog();
			if( apf.DialogResult == DialogResult.Cancel || apf.DPName==string.Empty)
				return ;
			IProjectComponent comp = new InfoClasses.ProjectComponentInfo();
			comp.ComponentName = apf.DPName;
			comp.ComponentAuthor = apf.Author;
			comp.ComponentSummary = apf.Summary;
			if( !this.subject.UpdateComponentDetails(pc,comp) )
			{
				ErrorMessage.Show("Class Diagram Details update failed, Check if a component with this name already exists.",
                    "Error");
                return;
            }
            /* If the component name was changed, update the tree view node. */
            if ( comp.ComponentName != selectedComponent )
            {
				DPAppData.MDIManager.ProjectTreeView.SelectedNode.Text = comp.ComponentName;
                /* Find the Child Component Window that was edited and change the component name */
                foreach (IMdiChild classDiaWnd in DPAppData.MDIManager.Documents)
                {
                    if (classDiaWnd.Project == this && classDiaWnd.Title == selectedComponent)
                    {
                        classDiaWnd.Title = comp.ComponentName;
                        break;
                    }
                }
                
				return;
			}
		}
	
		#endregion

	}
}
